package com.zenghus.spring.formework.context;

import com.sun.org.apache.xerces.internal.impl.xpath.regex.Match;
import com.zenghus.spring.demo.DemoController;
import com.zenghus.spring.formework.annotation.Autowired;
import com.zenghus.spring.formework.annotation.Controller;
import com.zenghus.spring.formework.annotation.Service;
import com.zenghus.spring.formework.aop.AopConfig;
import com.zenghus.spring.formework.bean.BeanDefinition;
import com.zenghus.spring.formework.bean.BeanPostProcessor;
import com.zenghus.spring.formework.bean.BeanWrapper;
import com.zenghus.spring.formework.context.support.BeanDefinitionReader;
import com.zenghus.spring.formework.core.BeanFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zenghu
 * @date 2018/5/1 12:07
 */
public class ApplicationContext extends DefaultListableBeanFactory implements BeanFactory {

    private String[] configLocations;

    private BeanDefinitionReader reader;

    //注册是单例容器
    private Map<String,Object> beanCachMap=new HashMap<String, Object>();

    //存储所有被代理的对象
    private Map<String,BeanWrapper> beanWrapperMap=new ConcurrentHashMap<String, BeanWrapper>();

    public ApplicationContext(String ...locations) {
        this.configLocations=locations;
        refresh();
    }

    public void refresh(){
        //1：定位
        this.reader=new BeanDefinitionReader(configLocations);

        //2：加载
        List<String> beanDefinitions=reader.loadBeanDefinitions();

        //3：注册
        doRegisty(beanDefinitions);

        //4：依赖注入（lazy-init=false）要执行依赖注入
        doAutorited();

        /*DemoController demoController= (DemoController) this.getBean("demoController");
        demoController.query("zenghu");*/

    }

    //开始执行自动化的依赖注入
    private void doAutorited(){
        for (Map.Entry<String,BeanDefinition> beanDefinitionEntry:this.beanDefinitionMap.entrySet()) {
            String beanName=beanDefinitionEntry.getKey();
            if(!beanDefinitionEntry.getValue().getLazyinit()){
                Object o= getBean(beanName);
                System.out.println(o.getClass());
            }
        }
    }

    public void populateBean(String beanName,Object instance){
        Class clazz= instance.getClass();

        //如果不是controller和service不做操作
        if(!(clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class))){
            return;
        }

        Field[] field= clazz.getDeclaredFields();
        for (Field field2 : field) {
            if(!field2.isAnnotationPresent(Autowired.class)){
                continue;
            }
            Autowired autowired=field2.getAnnotation(Autowired.class);
            String beanname=autowired.value().trim();
            if("".equals(beanname)){
                beanname=field2.getType().getName();
            }

            //给Autowired赋值
            field2.setAccessible(true);

            try {
                Object wrapperinstance=null;
                //递归解决实例化先后顺序问题
                if(this.beanWrapperMap.get(beanname)!=null){
                    wrapperinstance=this.beanWrapperMap.get(beanname).getWrapperInstance();
                }else{
                    wrapperinstance=getBean(beanname);
                }
                //通过反射强制赋值
                field2.set(instance, wrapperinstance);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            field2.setAccessible(false);
        }


    }

    //将beanDefinitions注册到beanDefinitionMap中
    private void doRegisty(List<String> beanDefinitions) {
        try{
            //循环所有扫描出来的类名，实例化，并且放到ioc容器中去
            for(String classname:beanDefinitions){
                //beanName有三种情况
                //1.默认是类名周字母小写
                //2.自定义名字
                //3.接口注入

                Class beanClass=Class.forName(classname);

                //如果是接口的话需要用实现类实例化
                if(beanClass.isInterface()){continue;}

                BeanDefinition beanDefinition=reader.registerBean(classname);
                if(beanDefinition!=null){
                    //放入ioc容器（首字母小写）
                    this.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
                }

                //给当前对象的所有接口赋值当前对象
                Class<?>[] interfaces=beanClass.getInterfaces();
                for (Class<?> i:interfaces) {
                    //如果多个实现类只能覆盖
                    //可以使用自定义名称
                    //spring会报错
                    this.beanDefinitionMap.put(i.getName(),beanDefinition);
                }

                //已经初始化完毕
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //依赖注入的开始
    //读取beanDefinition中的信息，通过反射机创建一个实例并返回
    //spring不会把最原始的对象放进去，会用一个BeanWrapper来进行一次包装
    //装饰器模式
    //1.保留原来的oop关系
    //2.需要对它进行扩展，增强（为以后的AOp打基础）
    @Override
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition=this.beanDefinitionMap.get(beanName);
        try{


            Object instance=instantionBean(beanDefinition);
            if(instance==null){
                return null;
            }

            if(this.beanWrapperMap.containsKey(beanName)){
                return this.beanWrapperMap.get(beanName).getWrapperInstance();
            }else {
                //生成通知事件
                BeanPostProcessor beanPostProcessor = new BeanPostProcessor();
                //在实例初始化以前调用一次
                beanPostProcessor.postProcessBeforeInitialization(instance, beanName);

                BeanWrapper beanWrapper = new BeanWrapper(instance);
                beanWrapper.setAopConfig(instantionAopConfig(beanDefinition));
                beanWrapper.setBeanPostProcessor(beanPostProcessor);
                this.beanWrapperMap.put(beanName, beanWrapper);

                //在实例初始化以后调用一次
                beanPostProcessor.postProcessAfterInitialization(instance, beanName);

                //给对象的参数赋值
                populateBean(beanName, instance);

                //通过这样调用，相当于给我们自己留有了可操作的空间
                return beanWrapperMap.get(beanName).getWrapperInstance();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private AopConfig instantionAopConfig(BeanDefinition beanDefinition) throws Exception{
        AopConfig aopConfig=new AopConfig();
        String expression=reader.getConfig().getProperty("pointCut");
        String[] before=reader.getConfig().getProperty("aspectBefore").split("\\s");
        String[] after=reader.getConfig().getProperty("aspectAfter").split("\\s");

        String className=beanDefinition.getBeanClassName();
        Class<?> clazz=Class.forName(className);

        Pattern pattern=Pattern.compile(expression);

        Class aspectClass=Class.forName(before[0]);

        for (Method m:clazz.getMethods()){
            //m.toString()=public java.lang.String com.zenghus.spring.demo.service.DemoService.get(java.lang.String)
            Matcher matcher=pattern.matcher(m.toString());
            if(matcher.matches()){
                //把我们满足切面规则的类添加到aop配置中
                aopConfig.put(m,aspectClass.newInstance(),new Method[]{aspectClass.getMethod(before[1]),aspectClass.getMethod(after[1])});
            }
        }

        return aopConfig;
    }

    //返回实例
    private Object instantionBean(BeanDefinition beanDefinition){
        Object instance=null;
        String className=beanDefinition.getBeanClassName();
        try{
            if(this.beanCachMap.containsKey(className)){
                instance=this.beanCachMap.get(className);
            }else{
                Class<?> clazz=Class.forName(className);
                instance=clazz.newInstance();
                this.beanCachMap.put(className,instance);
            }
            return instance;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    public String[] getBeanDefinittionNames(){
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public int getBeanDefinitionCount(){
        return this.beanDefinitionMap.size();
    }

    public Properties getConfig(){
        return this.reader.getConfig();
    }
}
